"""
Go
Copyright (C) 2007 Andrew Briscoe

A module used to describe a go game state.
"""

__license__ = 'GPL http://www.gnu.org/licenses/gpl.txt'
__author__ = 'Andrew Briscoe <everynothing@gmail.com>'
__version__ = '0.0.1'
__program__ = 'PIGS'

from Game import *

EVERYONE, NOBODY = 165456, 45681

class Stone(FilledPiece):
    'A go stone.'
    
    TYPE = 'Stone'

class DeadStone(FilledPiece):
    'A dead go stone.  Only for use at the end of the game'
    
    TYPE = 'DeadStone'

class Territory(Piece):
    'After the game as been scored, this is made of areas.'

    TYPE = 'Territory'

class Go(Board):
    'A State of a chess game.'
    
    GAME_TYPE       = 'Go'
    COLORS          = ['Black', 'White']
    POSSIBLE_BOARDS = ('9', '13', '19')
    POSSIBLE_PIECES = {'Stone': Stone, 'DeadStone': DeadStone, 'Territory': Territory}
    
    def __init__(self, board_name = 19 , komi = 6.5):
    
        size = int(board_name)
        Board.__init__(self, size, size, 2, str(board_name))
        self.passed     = NOBODY
        self.done       = NOBODY
        self.komi       = komi
        self.marks      = []
        self.territory  = None
        
    def gen_group(self, pos, colors):
        'A separate function so that marks gets reset.'
        
        group = self.plain_group_generator(pos, colors)
        self.marks = []
        return group
    
    def plain_group_generator(self, place, colors):
        'Yields each position that is connected to pos and meets test.'
        
        if self[place].color in colors and not place in self.marks:
            self.marks += [place]
            yield place
            for neigh in self._connections(place):
                for nxt in self.plain_group_generator(neigh.position, colors):
                    yield nxt
        
    def gen_next_to(self, places):
        'All the places next to places'
        
        for place in list(places):
            for con in self._connections(place):
                if not con.position in places:
                    yield con

    def score_game(self):
        """Scores the current game.  Creates a variable called territory, 
        and counts up each players. Dead stones count as a capture."""
        
        def what_color_next_to(squares):
            'Does the stone on the square in question have any liberties?'
        
            colors, found = NOBODY, False
            for con in [s for s in self.gen_next_to(squares) if not s.BLANK]:
                if not con.color in colors and not found:
                    colors = con.color
                    found = True
            return colors
        
        self.territory = [0] * self.players
        for square in self:
            territory = self.plain_group_generator(square, [None])
            owner = what_color_next_to(territory)
            if owner is not NOBODY:
                for pos in territory:
                    new_type = 'Territory' if self[pos].BLANK else 'DeadStone'
                    self._alter_piece_class(pos, new_type)
                self.territory[owner] += len(territory)
            if square.TYPE == 'DeadStone':
                self.captures[square.color].add_capture('Stone')

    def do_drop(self, the_drop):
        """Does the drop normally.  Then removes all of 
        opponents dead stones, and sets passed to nobody."""
        
        Board.do_drop(self, the_drop)
        self._remove_dead_stones(self.turn)
        self.passed = NOBODY
    
    def game_over(self):
        'Only a scored game is over, so check if territory has been set.'
        
        return not self.force_win and not not self.territory
    
    def gen_move(self, loc):
        'Generates a go move.'
        
        return Drop(loc, Stone(color=self.turn))
        
    def who_won(self):
        """If the win has not been forced, 
        return the player with the largest score."""
        
        if self.force_win is not None:
            return self.force_win
        return sorted([(self._score_of_player(p), p) \
           for p in xrange(self.players)])[-1][1]
    
    def _score_of_player(self, player):
        'How many points does player have?'
        
        if player == 1:
            komi = self.komi
        else:
            komi = 0
        return self.territory[player] + self.captures[player][0].ammount + komi

    def _connections(self, position):
        'In go only strait connections matter.'
        
        for square in Board.neighbors(self, position, STRAIT_DIRECTIONS):
            yield square
        
    def _remove_dead_stones(self, color):
        'Removes all the dead groups of color on the board.'
        
        for square in self:
            if square.color == color and not \
                self._any_liberties(square.position):
                for pos in self.gen_group(square.position, [color]):
                    self.capture(pos)
        self.marks = []
                
    def _any_liberties(self, square):
        'Does the stone on the square in question have any liberties?'
        
        piece = self[square]
        return not piece.BLANK and any([True for s in \
            self.gen_next_to(self.gen_group(square, [piece.color])) if s.BLANK])
    
    def is_drop_legal(self, drop):
        'Is drop legal on this board?'
        
        piece, pos = drop.piece, drop.position
        if not self[pos].BLANK or not piece == Stone(self.turn) \
            or self._in_scoring():
            return False
        self.place_piece(pos, piece)
        #Would it have liberties, or would it kill any opponent stones?
        move_ok = self._any_liberties(drop.position) or \
            any([not self._any_liberties(s.position) \
            for s in self._connections(pos) if s.color is self._next_player()])
        self._kill(pos)
        return move_ok
            
    def is_pass_legal(self):
        'Can the current player pass?'
        
        return not self._in_scoring()
    
    def do_pass(self):
        'Concatanates this player to passed and ends the turn.'
        
        if self.passed is self._next_player():
            self.passed = EVERYONE
        else:
            self.passed = self.turn
        self._end_turn()
    
    def _in_scoring(self):
        'Is it normal gameplay, or the scoring phase?'
        
        return self.passed == EVERYONE
            
    def do_mark(self, mark):
        """Mark the square at mark as a dead stone if not already, 
        and if it is make it alive."""
        
        to_mark = self[mark.position]
        if to_mark and not to_mark.BLANK:
            for pos in self.gen_group(mark.position, [None, to_mark.color]):
                if not self[pos].BLANK:
                    if self[pos].TYPE == 'Stone':
                        new_type = 'DeadStone'
                    else:
                        new_type = 'Stone'
                    self._alter_piece_class(pos, new_type)
                    if new_type is 'DeadStone':
                        self[pos].color = None
            if self.done == mark.player:
                self.done = NOBODY
            
    def do_done(self, done):
        'The player noted by done is finished scoring.'
        
        if self.done is not self._next_player():
            self.done = done.player
        else:
            self.done = EVERYONE
            self.score_game()
    
    def legal_drops(self, col = DEFAULT):
        'All the drops that are legal in self.'
        
        if col == DEFAULT:
            col = self.turn
        for square in self:
            if square.BLANK:
                drop = Drop(square.position, Stone(color=col))
                if self.is_drop_legal(drop):
                    yield drop
        
class Mark(Action):
    'Contains the information to describe a square mark.'
    
    def __init__(self, position=None):
        
        Action.__init__(self)
        self.position = position
        
    def execute(self, board): 
        'What happens when a player resigns.'
        
        board.do_mark(self)
    
    def legal(self, board):
        'A player can always resign.'
        
        return board._in_scoring()
        
class Done(Action):
    'A player is satisfied with the scoring.'
    
    def __init__(self, player):
        
        Action.__init__(self)
        self.player = player
        
    def execute(self, board):
        'What happens when a player resigns.'
        
        board.do_done(self)
    
    def legal(self, board):
        'A player can always resign.'
        
        return board._in_scoring()